import logging

from django import forms
from django.forms import models
from django.conf import settings
from django.http import QueryDict
from django.urls import path, reverse
from django.db.models import Q, ForeignKey, ManyToManyField, Model

from gideon.utils.pageination import Pagination
from oracle.base.tools import get_random_string
from oracle.base.baseview import render_handler, get_context_data, judge_render_flag

logger = logging.getLogger(__name__)


class Option(object):
    """
    选项类，为组合搜索服务
    """

    def __init__(self, field, db_condition=None, text_func=None, value_func=None, is_multi=False):
        """
        初始化
        :param field: 组合搜索的字段
        :param db_condition: 数据库关联查询时的条件
        :param text_func: 此函数用于显示组合搜索按钮页面文本
        :param value_func: 此函数用于显示组合搜索按钮的值
        :param is_multi: 是否支持多选
        """
        # 搜索的字段
        self.field = field
        # 显示字段的条件表达式
        self.db_condition = db_condition if db_condition else {}
        # 自定义标题方法
        self.text_func = text_func
        # 自定义数值方法
        self.value_func = value_func
        # 是否是多参数
        self.is_multi = is_multi
        # 当前数据类型，默认都是queryset即FK/M2M，显示的时候会检测可能会变更为choice类型
        self.data_type = 'queryset'
        # 请求url的参数
        self.params = dict()

    def get_db_condition(self, *args, **kwargs):
        """
        获取数据搜索条件
        :param args:
        :param kwargs:
        :return:
        """
        return self.db_condition

    def get_queryset_or_tuple(self, request, model_class, *args, **kwargs):
        """
        根据字段去获取数据库关联的数据
        :param request:
        :param model_class:
        :param args:
        :param kwargs:
        :return:
        """
        # 格式化数据变量申请
        search_group_row = {
            # 快速筛选大类名称
            "title": "",
            # 快速筛选包含的每一项
            "sub": [],
        }
        # 组合搜索生成 URL 逻辑使用, 初始化params
        params = request.GET
        # copy原数据，防止修改原来的数据
        total_query_dict = params.copy()
        # 设置数据为可修改状态
        total_query_dict._mutable = True
        # 获取是否选取了本类数据
        origin_value_list = params.getlist(self.field)
        # 没有选取本类数据，说明是全部显示
        if not origin_value_list:
            search_group_row["sub"].append({
                "href": f"?{total_query_dict.urlencode()}",
                "active": True,
                "title": "全部",
            })
        else:
            # 剔除本类别限定条件即是选择全部
            total_query_dict.pop(self.field)
            search_group_row["sub"].append({
                "href": f"?{total_query_dict.urlencode()}",
                "active": False,
                "title": "全部",
            })
        # 根据字符串获取自己对应的Model类，在类中找到对应的字段对象
        field_object = model_class._meta.get_field(self.field)
        # 使用字段定义的verbose_name作为显示值
        search_group_row["title"] = field_object.verbose_name

        # 再根据对象去获取关联数据
        if isinstance(field_object, ForeignKey) or isinstance(field_object, ManyToManyField):
            # FK和M2M应该去获取器关联表中的数据
            db_condition = self.get_db_condition(*args, **kwargs)
            # 获取queryset数据
            option_data = field_object.remote_field.model.objects.filter(**db_condition)
        else:
            # 获取元组类型的数据
            self.data_type = 'tuple'
            # 获取choice数据
            option_data = field_object.choices

        for item in option_data:
            # 获取页面显示的文本
            text = self.get_text(item, self.text_func)
            # 获取组合搜索按钮文本背后对应的值
            # 获取需要添加的值
            value = str(self.get_value(item, self.value_func))
            # TODO 之前已经获取一次，而且这里也是重复获取，效率有问题，留着改进
            # 每一次保证数据完整，都需要重新获取数据
            # 获取当前request请求参数
            query_dict = params.copy()
            # 设置参数可变
            query_dict._mutable = True
            # 获取是否选取了本类数据
            if self.is_multi:
                origin_value_list = params.getlist(self.field)
            else:
                origin_value_list = params.get(self.field)
                if origin_value_list:
                    origin_value_list = [origin_value_list, ]
                else:
                    origin_value_list = list()
            if self.is_multi:
                # 说明本次是点击的当前的按钮
                if value in origin_value_list:
                    # 达到再点击返回全部按钮的效果
                    origin_value_list.remove(value)
                    # 重新组合参数
                    query_dict.setlist(self.field, origin_value_list)
                    # 设置突出显示标志位
                    active = True
                else:
                    # 添加当前值，如果点击本按钮就可以将本按钮值进行传入目的
                    origin_value_list.append(value)
                    query_dict.setlist(self.field, origin_value_list)
                    active = False
            else:
                # 字段值设置为本按钮的值
                query_dict[self.field] = value
                # 点击的本按钮
                if value in origin_value_list:
                    # 剔除数值，再点击是全部的目的
                    query_dict.pop(self.field)
                    active = True
                else:
                    active = False
            # 设置格式化参数
            search_group_row["sub"].append({
                # 访问的url
                "href": f"?{query_dict.urlencode()}",
                # 是否突出显示
                "active": active,
                # 按钮标题
                "title": text,
            })
        return search_group_row

    def get_value(self, field_object, value_func=None):
        """
        获取数值信息
        :param field_object: 字段对象
        :param value_func: 自定义方法
        :return:
        """
        # 如果有自定义显示方法，使用自定义显示，前加CU标志
        if value_func:
            return f"CU{value_func(field_object, get_condition=False)}"
        # 因为使用key作为索引，所以list页面处理时不能通过外键id查找，所以添加标志，外键与choice处理方法不同
        # choice类型数据数值前加 CE(choice 首尾字母大写) 标识
        if self.data_type == "tuple":
            return f"CE{field_object[0]}"
        # FK或者M2M方式前加 FM(FK和M2M首字母) 标志
        return f"FM{field_object.key}"

    def get_text(self, field_object, text_func=None):
        """
        获得显示信息
        :param field_object: 字段对象
        :param text_func: 自定义方法
        :return:
        """
        if text_func:
            return text_func(field_object)
        if self.data_type == "tuple":
            return field_object[1]
        return str(field_object)


class GideonForm(forms.Form):
    """
    通用Form信息
    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        last_fields = {"status": None, "level": None}
        for name, field in self.fields.items():
            if name in last_fields:
                last_fields[name] = field
            field.widget.attrs['class'] = 'form-control'
        # 通过删除添加调整顺序
        for name, value in last_fields.items():
            if value:
                del self.fields[name]
                self.fields[name] = value


class GideonModelForm(forms.ModelForm):
    """
    通用ModelForm信息
    """

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # 因为 status 和 level 是继承而来的，所以顺序会比较靠前，需要进行调整
        last_fields = {"status": None, "level": None}

        # 添加默认前端class属性，并且把需要调整顺序的值取出
        for name, field in self.fields.items():
            if name in last_fields:
                last_fields[name] = field
            field.widget.attrs['class'] = 'form-control'
        # 通过删除添加调整顺序
        for name, value in last_fields.items():
            if value:
                del self.fields[name]
                self.fields[name] = value


def custom_handler_view_init(handler_view_type="custom_handler_view"):
    def custom_handler_view(origin_func):
        def wrapper(self, request, handler_view_type_inner=handler_view_type, *args, **kwargs):
            """
            自定义页面处理
            :param self:
            :param handler_view_type_inner: 内部传入处理方法标志位
            :param request:
            :param args:
            :param kwargs:
            :return:
            """
            h_v_t = self.get_handler_view_type(handler_view_type_inner)
            if not h_v_t:
                h_v_t = "custom_handler_view"
            # 全局初始化， 根据handler参数也可以分handler初始化不同内容
            self.initialize(request, handler=h_v_t, *args, **kwargs)
            status = "success"
            data = {
                "operation": "custom_handler_view",
            }

            # status, method=None, template=None, msg=None, *args, **kwargs
            origin_func_result = origin_func(self, request, data=data, handler_view_type=handler_view_type, *args,
                                             **kwargs)

            status = origin_func_result.get("status", status)
            method = origin_func_result.get("method", None)
            template = origin_func_result.get("template", None)
            msg = origin_func_result.get("msg", None)
            args = origin_func_result.get("args", ())
            kwargs = origin_func_result.get("kwargs", {})

            self.custom_response_data(data, handler_view_type, *args, **kwargs)
            return render_handler(request, status, method, template, data, msg, *args, **kwargs)

        return wrapper

    return custom_handler_view


class GideonHandler(object):
    # 批量操作列表
    action_list = ["delete", ]
    # 显示列设置，表头也是根据此列进行显示
    display_list = list()
    # 通用管理按钮
    display_manager_button = ['add', ]
    # 列表最后操作项的显示
    # display_operation_button = ['edit', 'delete']
    display_operation_button = ['options']
    # 每页显示条数
    per_page_count = 20
    # 排序用列表
    order_list = list()
    # 组合搜索列表
    search_group = list()
    # 模糊搜索列表，内容为django的models模糊查找语法字符串
    search_list = list()
    list_template = str()
    add_template = str()
    change_template = str()
    delete_template = str()

    def __init__(self, singleton_instance, model_class, prev, model_form_class):
        """
        流程类初始化
        :param singleton_instance: 单例模式实例
        :param model_class: 数据库类
        :param prev: 网页添加自定义字段
        :param model_form_class: 表单验证类
        """
        self.site = singleton_instance
        self.model_class = model_class
        self.prev = prev
        self.model_form_class = model_form_class
        self.request = None
        # 内部渲染使用参数
        self.context = dict()

    def action_multi_delete(self, title=False, url=None, data=None, *args, **kwargs):
        """
        批量删除(如果想要定制执行成功后的返回值，那么就为action函数设置返回值即可)
        :param title: 显示标题标志
        :param url: 返回成功跳转的url
        :param data: 渲染以及操作需要的数据
        :param args:
        :param kwargs:
        :return:
        """
        if data is None:
            data = dict()
        if title:
            return '批量删除'
        else:
            key_list = self.request.POST.getlist('key')
            self.model_class.objects.filter(key__in=key_list).delete()
            if not url:
                url = self.request.get_full_path
            status = "success"
            data.update(
                {
                    "head_title": "批量删除成功",
                    "title": "数据批量删除成功",
                    "href": url,
                    "button_text": "返回列表页"
                }
            )
        return status

    def change_form_data_type(self, data, *args, **kwargs):
        """
        转化form数据为可以json格式化的数据类型
        :param data: 包含form的data数据
        :param args:
        :param kwargs:
        :return: data: 最终数据，不会显示返回
        """
        # 是否强制外部渲染标志
        force_external_render = judge_render_flag(self.request, **kwargs)

        if force_external_render == 0:
            data.update(self.get_context_data(*args, **kwargs))
        elif force_external_render == 1:
            form = dict()
            index = 0
            for item in data["form"]:
                form[index] = {
                    "name": item.name,
                    "help_text": item.field.help_text,
                    "initial": item.field.initial,
                    "label": item.field.label,
                    "required": item.field.required,
                    "error": item.errors,
                }
                if hasattr(item.field, "choices"):
                    # 外键或M2M情况，查询到的是queryset数据，需要进行处理
                    if isinstance(item.field.choices, models.ModelChoiceIterator):
                        form[index]["choices"] = [(i.key, str(i),) for i in item.field.choices.queryset]
                    elif callable(item.field.choices):
                        form[index]["choices"] = item.field.choices()
                    else:
                        form[index]["choices"] = item.field.choices
                # 新建时instance没有意义，只修改时获取
                # TODO 极度疲惫的时候完成，没有考虑最优解
                if data.get("operation") == "change":
                    form[index]["instance"] = getattr(data["form"].instance, item.name)
                    if isinstance(form[index]["instance"], list):
                        form[index]["instance"] = [str(item) for item in form[index]["instance"]]
                    elif isinstance(form[index]["instance"], Model):
                        form[index]["instance"] = str(form[index]["instance"])
                index += 1
            data["form"] = form

    def custom_response_data(self, data, handler_type=None, *args, **kwargs):
        pass

    def format_datetime_field(self, obj, field, time_format='%Y-%m-%d', is_title=False, *args, **kwargs):
        """
        定制时间格式的数据
        :param obj: 对象
        :param field: 字段名称
        :param time_format: 要格式化的时间格式
        :param is_title: 格式化字符串显示的标题
        :param args:
        :param kwargs:
        :return:
        """
        if is_title:
            return self.model_class.get_verbose_name(field)
        datetime_value = getattr(obj, field)
        if datetime_value:
            return datetime_value.strftime(time_format), "body", "datetime"
        return "--", "body", "datetime"

    def format_m2m_field(self, obj, field, is_title=False, *args, **kwargs):
        """
        显示m2m文本信息
        :param obj: 对象
        :param field: 字段名称
        :param is_title: m2m字段显示的标题
        :param args:
        :param kwargs:
        :return:
        """
        if is_title:
            return self.model_class.get_verbose_name(field)
        queryset = getattr(obj, field).all()
        text_list = [str(row) for row in queryset]
        if text_list:
            return ','.join(text_list), "body", "m2m"
        return "--", "body", "m2m"

    def get_action_list(self, action_list=None, *args, **kwargs):
        """
        批量操作内容
        :param action_list:
        :param args:
        :param kwargs:
        :return:
        """
        if action_list is not None or not isinstance(action_list, list):
            action_list = list()
        if self.action_list:
            for key in self.action_list:
                if hasattr(self, f"action_multi_{key}"):
                    action_list.append(getattr(self, f"action_multi_{key}"))
        return action_list

    def get_context_data(self, *args, **kwargs):
        """
        获取内部渲染额外参数
        :param args:
        :param kwargs:
        :return:
        """
        # 自定制网站信息，如果没有则默认使用oracle模块配置
        # 主要设置项有以下几个
        # 'website_title' 网站标题
        # 'website_description' 网站描述
        # 'website_welcome' 网站欢迎标语
        # 'website_author' 网站作者
        get_context_data(self.request, logger, self.context)
        return self.context

    def get_display_list(self, value=None):
        """
        获取页面上应该显示的列
        :param value: 自定义显示信息
        :return: 显示字段信息
        """
        # 如果传入显示信息不合法，则重置为空列表
        if not value or not isinstance(value, list):
            value = list()
        # 扩展显示信息
        value.extend(self.display_list)
        return value

    def get_extra_urls(self):
        """
        扩展的urls内容
        :return: 路由列表，格式如下:
            [
                path('router/<slug:param>', handler),
                .......
            ]
        """
        return []

    def get_handler_view_type(self, handler_view_type, *args, **kwargs):
        """
        让handle的操作flag可以自定义
        :param handler_view_type: 传入的标记，增删改查操作传入固定字符串
            默认 handler_view_type 参数:
                列表页面: list
                添加页面: add
                修改页面: change
                删除页面: delete
        :param args:
        :param kwargs:
        :return:
        """
        return handler_view_type

    def get_handler_add_view_type(self, *args, **kwargs):
        return self.get_handler_view_type("add", *args, **kwargs)

    def get_handler_change_view_type(self, *args, **kwargs):
        return self.get_handler_view_type("change", *args, **kwargs)

    def get_handler_delete_view_type(self, *args, **kwargs):
        return self.get_handler_view_type("delete", *args, **kwargs)

    def get_handler_list_view_type(self, *args, **kwargs):
        return self.get_handler_view_type("list", *args, **kwargs)

    def get_model_form(self, handler_type, model_form_class, instance=None, *args, **kwargs):
        """
        实例化model form， 因为编辑页面有初始化问题，统一在此处进行实例化
        :param handler_type: 定制添加和编辑页面model form, 添加传入"add" 修改传入"change"
        :param model_form_class: 页面model form类
        :param instance: 一般为编辑页面传入的当前编辑的实例
        :param args:
        :param kwargs:
        :return:
        """
        if handler_type == "add":
            if self.request.method == 'GET':
                return model_form_class()
            elif self.request.method == 'POST':
                return model_form_class(data=self.request.POST)
        elif handler_type == "change":
            if self.request.method == 'GET':
                return model_form_class(instance=instance)
            elif self.request.method == 'POST':
                return model_form_class(data=self.request.POST, instance=instance)
        else:
            # 未定义处理方法，一律按照 add 中 get 方式返回
            return model_form_class()

    def get_model_form_class(self, handler_type=None, *args, **kwargs):
        """
        获取model form
        :param handler_type: 定制添加和编辑页面, 添加传入"add" 修改传入"change"
        :param args:
        :param kwargs:
        :return:
        """
        if self.model_form_class:
            return self.model_form_class

        class DynamicModelForm(GideonModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        if handler_type:
            return DynamicModelForm
        else:
            # 未清楚定义，目前返回有问题
            return GideonModelForm

    def get_prev(self):
        """
        生成url前缀
        :return:
        """
        return self.prev

    def get_queryset(self, handle_type='list', *args, **kwargs):
        """
        获取初始数据集
        :param handle_type: 处理方法， 处理方法不同，返回不同
            默认 handle_type 参数:
                列表页面: list
                添加页面: add
                修改页面: change
                删除页面: delete
        :param args:
        :param kwargs:
        :return:
        """
        if handle_type == "list":
            return self.model_class.objects.filter(~Q(status=2))
        elif handle_type == "change":
            key = kwargs.get('key')
            return self.model_class.objects.filter(Q(key=key) & ~Q(status=2)).first()
        elif handle_type == "delete":
            key = kwargs.get('key')
            return self.model_class.objects.filter(Q(key=key) & ~Q(status=2)).first()
        else:
            return self.model_class.objects.filter(~Q(status=2))

    def get_order_list(self):
        """
        数据排序方式
        :return:
        """
        return self.order_list or ['level', 'status', 'id']

    def get_search_group(self):
        """
        组合搜索钩子函数
        :return:
        """
        return self.search_group

    def get_url_name(self, param):
        """
        获取url名称
        :param param: 名字标识。同一个 model 根据 param 不同名字不同
            默认情况下，param参数如下：
                列表页面: list
                添加页面: add
                修改页面: change
                删除页面: delete
        :return: 组合的名字，不同 model 前缀不同，同一个 model 根据 param 区分
        """
        # 获得model所属app名称
        app_label = self.model_class.get_app_label(alias=False)
        # 获得model本身name名称
        model_name = self.model_class.get_model_name(alias=False)
        # 组合名称作为基本名称
        base_name = f'{app_label}_{model_name}'
        # 如果有 url 前缀还要添加前缀
        if self.prev:
            base_name = f'{base_name}_{self.prev}'
        # 根据传入字段生成不同的名字
        return f'{base_name}_{param}'

    def get_url_name_add(self, param='add'):
        """
        添加url的名字
        :param param:
        :return:
        """
        return self.get_url_name(param)

    def get_url_name_change(self, param='change'):
        """
        编辑url的名字
        :param param:
        :return:
        """
        return self.get_url_name(param)

    def get_url_name_delete(self, param='delete'):
        """
        删除url的名字
        :param param:
        :return:
        """
        return self.get_url_name(param)

    def get_url_name_list(self, param='list'):
        """
        展示url的名字
        :param param:
        :return:
        """
        return self.get_url_name(param)

    def get_urls(self):
        # name格式 gideon:{app_name}_{model_name}_{prev}_{list/add/change/del}
        patterns = [
            path(r'list/', self.handler_view_list, name=self.get_url_name_list()),
            path(r'add/', self.handler_view_add, name=self.get_url_name_add()),
            path(r'change/<slug:key>/', self.handler_view_change, name=self.get_url_name_change()),
            path(r'delete/<slug:key>/', self.handler_view_delete, name=self.get_url_name_delete()),
        ]
        patterns.extend(self.get_extra_urls())
        return patterns

    def get_search_group_condition(self):
        """
        获取组合搜索条件
        :return:
        """
        condition = dict()
        for option in self.get_search_group():
            if option.is_multi:
                values_list = self.request.GET.getlist(option.field)
                if not values_list:
                    continue
                data_type = "queryset"
                real_value_list = list()
                for value in values_list:
                    if value.startswith("CE"):
                        real_value_list.append(value[2::])
                        data_type = "choice"
                    elif value.startswith("FM"):
                        real_value_list.append(value[2::])
                    elif value.startswith("CU"):
                        real_value_list.append(value[2::])
                        data_type = "custom"
                if data_type == "choice":
                    condition[f'{option.field}__in'] = real_value_list
                elif data_type == "queryset":
                    condition[f"{option.field}__key__in"] = real_value_list
                elif data_type == "custom":
                    option.value_func(condition=condition, value=real_value_list, get_condition=True)
            else:
                value = self.request.GET.get(option.field)
                if not value:
                    continue
                # choice类型数据
                if value.startswith("CE"):
                    condition[option.field] = value[2::]
                # TODO M2M没有测试过
                # FK或M2M类型数据
                elif value.startswith("FM"):
                    condition[f"{option.field}__key"] = value[2::]
                # 自定义类型数据
                elif value.startswith("CU"):
                    # condition[f"{option.field}"] = value[2::]
                    option.value_func(condition=condition, value=value[2::], get_condition=True)

        return condition

    def get_search_list(self):
        """
        模糊查找可以搜索的内容
        :return:
        """
        return self.search_list

    def get_special_fields_show_name(self):
        """
        如果显示特殊字段并自定义显示名字，在这里进行修改
        :return: 特殊字段显示名字的字典
        """
        show_name = dict()
        display_list = self.get_display_list()
        special_fields = ["key", "status", "level"]
        for field in special_fields:
            if field in display_list:
                show_title = getattr(self.model_class, f"get_{field}_show_name")()
                show_name[field] = show_title
        return show_name

    def get_template_names(self, handler=None, *args, **kwargs):
        """
        render template 的名字
        :param handler: 处理的方法
            默认分为以下几种:
                列表页面: list
                添加页面: add
                修改页面: change
                删除页面: delete
        :param args:
        :param kwargs:
        :return:
        """
        if handler == "list":
            template = getattr(self, 'list_template')
            if not template:
                template = "gideon/model_list.html"
        elif handler == "add":
            template = getattr(self, 'add_template')
            if not template:
                template = 'gideon/change.html'
        elif handler == "change":
            template = getattr(self, 'change_template')
            if not template:
                template = 'gideon/change.html'
        elif handler == "delete":
            template = getattr(self, 'delete_template')
            if not template:
                template = 'gideon/delete_confirm.html'
        else:
            template = 'oracle/notice.html'
        return template

    def handler_view_add(self, request, *args, **kwargs):
        """
        添加页面处理
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        handler_view_type = self.get_handler_add_view_type(*args, **kwargs)
        if not handler_view_type:
            handler_view_type = "add"
        self.initialize(request, handler=handler_view_type, *args, **kwargs)
        status = "success"
        data = {
            # 需要使用的form表单
            "form": None,
            "form_name": "创建新记录",
            "form_description": "",
            # origin_list_url
            "return_url": self.reverse_url_list(request, *args, **kwargs),
            "operation": handler_view_type,
            "show_field": self.get_special_fields_show_name()
        }

        template = None

        model_form_class = self.get_model_form_class(handler_view_type, *args, **kwargs)
        data["form"] = self.get_model_form(handler_view_type, model_form_class, *args, **kwargs)
        if request.method == "GET":
            template = self.get_template_names(handler_view_type)
            self.change_form_data_type(data, *args, **kwargs)
        elif request.method == "POST":
            if data["form"].is_valid():
                response = self.save(data["form"], *args, **kwargs)
                if response:
                    # 数据库保存成功跳转回列表页面
                    list_url = self.reverse_url_list(request, *args, **kwargs)
                    del data["form"]
                    data.update(
                        {
                            "head_title": "创建成功",
                            "title": "数据创建成功",
                            "href": list_url,
                            "button_text": "返回列表页"
                        }
                    )
                else:
                    # 数据库保存失败则进行提示
                    status = "form_save_failed"
                    data.update(
                        {
                            "head_title": "创建失败",
                            "title": "数据创建后保存失败",
                        }
                    )
            else:
                template = self.get_template_names(handler_view_type)
                status = "model_valid_check_failed"
                self.change_form_data_type(data, *args, **kwargs)

                for item in data["form"]:
                    key = item.name
                    if key in request.POST:
                        item.instance = request.POST.get(key)
                data.update({
                    "head_title": "添加数据不合法",
                    "title": "输入的新建信息不合法，请验证后重新操作！",
                    "href": data["return_url"],
                    "button_text": "返回列表页",
                })
        self.custom_response_data(data, handler_view_type, *args, **kwargs)
        return render_handler(request, status, template=template, data=data, *args, **kwargs)

    def handler_view_change(self, request, key, *args, **kwargs):
        """
        修改页面处理
        :param request:
        :param key: 修改数据索引值
        :param args:
        :param kwargs:
        :return:
        """
        handler_view_type = self.get_handler_change_view_type(*args, **kwargs)
        if not handler_view_type:
            handler_view_type = "change"
        self.initialize(request, handler=handler_view_type, *args, **kwargs)
        status = "success"
        data = {
            "form": None,
            "form_name": "",
            "form_description": "",
            "return_url": self.reverse_url_list(request, *args, **kwargs),
            "operation": handler_view_type,
            "show_field": self.get_special_fields_show_name()
        }

        template = None

        current_change_object = self.get_queryset(handler_view_type, key=key, *args, **kwargs)
        data['form_name'] = f'编辑 {current_change_object}'
        if current_change_object:
            model_form_class = self.get_model_form_class(handler_view_type, *args, **kwargs)
            data["form"] = self.get_model_form(handler_view_type, model_form_class, instance=current_change_object,
                                               *args,
                                               **kwargs)
            if request.method == "GET":
                template = self.get_template_names(handler_view_type)
                self.change_form_data_type(data, *args, **kwargs)
            elif request.method == 'POST':
                if data["form"].is_valid():
                    response = self.save(data["form"], is_update=True, *args, **kwargs)
                    if response:
                        del data["form"]
                        # 数据库保存成功跳转回列表页面
                        data.update(
                            {
                                "head_title": "修改成功",
                                "title": "数据修改成功",
                                # TODO 统一href与return_url
                                "href": data["return_url"],
                                "button_text": "返回列表页"
                            }
                        )
                    else:
                        # 数据库保存失败进行提示
                        status = "form_save_failed"
                        data.update(
                            {
                                "head_title": "修改失败",
                                "title": "数据修改后保存失败",
                            }
                        )
                else:
                    template = self.get_template_names(handler_view_type)
                    status = "model_valid_check_failed"
                    self.change_form_data_type(data, *args, **kwargs)
                    data.update({
                        "head_title": "修改数据不合法",
                        "title": "输入的更新数据不合法，请验证后重新操作！",
                        "href": data["return_url"],
                        "button_text": "返回列表页",
                    })
        # 没有数据有问题，报错
        else:
            status = "change_object_not_exist"
            data.update({
                "head_title": "修改数据错误",
                "title": "数据不存在！",
                "href": data.get("return_url"),
                "button_text": "返回列表页",
            })
        self.custom_response_data(data, handler_view_type, *args, **kwargs)
        return render_handler(request, status, template=template, data=data, *args, **kwargs)

    def handler_view_delete(self, request, key, *args, **kwargs):
        """
        删除页面处理
        :param request:
        :param key: 删除数据索引值
        :param args:
        :param kwargs:
        :return:
        """
        handler_view_type = self.get_handler_delete_view_type(*args, **kwargs)
        if not handler_view_type:
            handler_view_type = "delete"
        # 初始化
        self.initialize(request, handler=handler_view_type, *args, **kwargs)
        # 定义状态
        status = "success"
        # 定义数据
        data = {
            "return_url": self.reverse_url_list(request, *args, **kwargs),
            "form": None,
            "operation": handler_view_type,
        }
        template = None

        # 当前删除数据
        current_delete_object = self.get_queryset(handler_view_type, key=key, *args, **kwargs)
        # 如果数据存在
        if current_delete_object:
            if request.method == "GET":
                model_form_class = self.get_model_form_class(handler_view_type, *args, **kwargs)
                # 复用change中的get_model_form逻辑
                data["form"] = self.get_model_form('change', model_form_class, instance=current_delete_object, *args,
                                                   **kwargs)
                template = self.get_template_names(handler_view_type)
                self.change_form_data_type(data, *args, **kwargs)
            elif request.method == 'POST':
                response = current_delete_object.delete()
                if response:
                    # 数据库删除成功跳转回列表页面
                    data.update(
                        {
                            "head_title": "删除成功",
                            "title": "数据删除成功",
                            "href": data["return_url"],
                            "button_text": "返回列表页"
                        }
                    )
                else:
                    # 数据库删除失败进行提示
                    status = "object_delete_failed"
                    data.update(
                        {
                            "head_title": "删除失败",
                            "title": "数据删除失败",
                        }
                    )
        # 如果数据不存在，进行报错
        else:
            status = "delete_object_not_exist"
            data.update({
                "head_title": "删除数据错误",
                "title": "数据不存在！",
                "href": data.get("return_url"),
                "button_text": "返回列表页",
            })
        self.custom_response_data(data, handler_view_type, *args, **kwargs)
        return render_handler(request, status, template=template, data=data, *args, **kwargs)

    def handler_view_list(self, request, *args, **kwargs):
        """
        列表页面处理
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        handler_view_type = self.get_handler_list_view_type(*args, **kwargs)
        if not handler_view_type:
            handler_view_type = "list"
        # 全局初始化， 根据handler参数也可以分handler初始化不同内容
        self.initialize(request, handler=handler_view_type, *args, **kwargs)
        status = "success"
        data = {
            # 显示列表头
            "header_list": list(),
            # 显示表格主体
            "body_list": list(),
            # 分页数据
            "page_list": None,
            # 显示按钮信息
            "show_buttons": list(),
            # 模糊查找列表
            "search_list": self.get_search_list(),
            # 传入模糊查找的值， 之后会填充到页面上
            "search_value": request.GET.get('q'),
            # 批量操作列表
            "action_dict": None,
            # 组合搜索列表
            "search_group": list(),
            "operation": handler_view_type,
        }
        template = None

        # 获取显示信息
        display_list = self.get_display_list()
        if display_list:
            # 1 处理action
            # 批量操作必须有checkbox 否则没有办法进行操作
            action_dict = dict()
            if "checkbox" in display_list:
                action_list = self.get_action_list()
                for item in action_list:
                    # 格式化字典变量 格式大致为 {函数名称: 标题}
                    title = item(title=True)
                    if title:
                        action_dict[item.__name__] = title

            if request.method == 'POST':
                # 获取operation选项
                action_func_name = self.request.POST.get('operation')
                # 如果获得的批量操作名称合法
                if action_func_name and action_func_name in action_dict:
                    # 如果批量函数
                    if hasattr(self, action_func_name):
                        # 执行批量操作函数
                        status = getattr(self, action_func_name)(data=data, *args, **kwargs)
                        data.update(data)
                else:
                    status = "action_params_error"
                    # 批量操作没有收到有效参数，提示后跳转回列表页面
                    data.update(
                        {
                            "head_title": "批量操作错误",
                            "title": "批量操作参数错误",
                            "href": request.get_full_path(),
                            "button_text": "返回列表页"
                        }
                    )
            elif request.method == 'GET':
                # 组合模糊查找条件
                conn = Q()
                conn.connector = "OR"
                if data["search_value"]:
                    for item in data["search_list"]:
                        conn.children.append((item, data["search_value"]))
                # 2. 获取排序
                order_list = self.get_order_list()
                # 获取显示的queryset信息
                prev_queryset = self.get_queryset(handler_view_type, *args, **kwargs)
                search_group_condition = self.get_search_group_condition()
                queryset = prev_queryset.filter(conn).filter(**search_group_condition).order_by(*order_list).distinct()
                # 3 处理分页
                # 目前总共有多少条数据
                all_count = queryset.count()
                # 获取get参数值，并设置为可改变
                query_params = request.GET.copy()
                query_params._mutable = True
                # 创建分页实例
                page = Pagination(
                    current_page=request.GET.get("page"),
                    all_count=all_count,
                    base_url=request.path_info,
                    query_params=query_params,
                    per_page=self.per_page_count,
                )
                # 4. 处理表格的表头
                # 定义显示的按钮样式，也可以定义table显示样式
                # kwargs.update(self.set_button_style(*args, **kwargs))
                # 循环定义的display_list
                for key in display_list:
                    # 如果有反射show_{key}_field 执行获取title方法
                    if hasattr(self, f'show_{key}_field'):
                        verbose_name = getattr(self, f'show_{key}_field')(is_title=True, *args, **kwargs)
                    # 否则返回字段本身的 verbose_name 属性
                    else:
                        verbose_name = self.model_class.get_verbose_name(key)
                    # 将获取的信息添加到数据中
                    data["header_list"].append(verbose_name)
                # 最后的操作列定义
                if self.display_operation_button:
                    for key in self.display_operation_button:
                        if hasattr(self, f'show_{key}_button'):
                            # 返回字符串类型标题名字
                            title = getattr(self, f'show_{key}_button')(*args, **kwargs)
                            if title:
                                # 将标题名字添加到头列表中
                                data["header_list"].append(title)

                # 5. 处理表格的内容
                display_queryset = queryset[page.start:page.end]
                for item in display_queryset:
                    # 定义临时数组，格式化显示信息之后整体加入到body_list中
                    item_list = list()
                    # 循环显示的字段
                    for key in display_list:
                        # 如果有反射u 执行获取 body 方法
                        if hasattr(self, f'show_{key}_field'):
                            value, value_type, addition = getattr(self, f'show_{key}_field')(obj=item, *args, **kwargs)
                        # 否则，如果是 FK 或 M2M 格式获取中文显示字段
                        elif hasattr(item, f'get_{key}_display'):
                            # 最终全部会格式化为 choice类型显示中文信息
                            value = getattr(item, f'get_{key}_display')()
                            value_type = "body"
                            addition = {"type": "FM"}
                        # 如果都不是，则显示定义的内容
                        else:
                            text = getattr(item, key)
                            if isinstance(text, Model):
                                text = str(text)
                            # 如果没有text 并且text不是数字(排除text=0的情况)，这显示空位符
                            if not isinstance(text, int) and not text:
                                text = "--"
                            # 组合临时变量
                            value = text
                            value_type = "body"
                            addition = {"type": "field"}
                        item_list.append({"key": key, "data": value, "type": value_type, "addition": addition})
                    # 最后操作列需要的 url
                    if self.display_operation_button:
                        for key in self.display_operation_button:
                            if hasattr(self, f'show_{key}_button'):
                                # 获取operation操作按钮的必要数据
                                operation = getattr(self, f'show_{key}_button')(is_header=False, obj=item, *args,
                                                                                **kwargs)
                                # 如果是字典说明是单个操作按钮，整理成列表
                                if isinstance(operation, dict):
                                    operation = [operation, ]
                                # 每一个operation定义，对应一个操作列
                                if isinstance(operation, list):
                                    item_list.append({"data": operation, "type": "operation"})
                    # 将整体的行列表添加到列表中
                    data["body_list"].append(item_list)
                # 6. 管理按钮显示
                for button in self.display_manager_button:
                    if hasattr(self, f'show_{button}_button'):
                        button_info = getattr(self, f'show_{button}_button')(is_header=False, obj=True, *args, **kwargs)
                        if button_info:
                            data["show_buttons"].append(button_info)
                # 获取定义的 search_group， 内容为 Option 类
                search_group = self.get_search_group()
                for option_object in search_group:
                    row = option_object.get_queryset_or_tuple(request, self.model_class, *args, **kwargs)
                    data["search_group"].append(row)
                # 最后处理页面
                data["page_list"] = page.page_data()
                # 最后存储批量操作字典
                data["action_dict"] = action_dict
        else:
            data["header_list"].append(self.model_class.get_model_name())

        # 是否强制外部渲染标志
        force_external_render = judge_render_flag(request, **kwargs)

        if force_external_render == 0:
            data.update(self.get_context_data(*args, **kwargs))
            if request.method == 'GET':
                template = self.get_template_names(handler_view_type)
        self.custom_response_data(data, handler_view_type, *args, **kwargs)
        return render_handler(request, status, template=template, data=data, *args, **kwargs)

    def initialize(self, request, *args, **kwargs):
        """
        全局初始化
        :param request:
        :param args:
        :param kwargs:
            可以根据传入handler不通进行不同的初始化
            默认情况下，handler参数如下：
                列表页面: list
                添加页面: add
                修改页面: change
                删除页面: delete
        :return:
        """
        self.request = request

    def reverse_handle_url(self, request, name, *args, **kwargs):
        """
        生成带原搜索条件的url
        :param request: 请求对象
        :param name: 路由的name属性，怎么生成的url使用同样的方法生成name
        :param args:
        :param kwargs:
        :return: 带原有搜索结果的url
        """
        name = f"{self.site.namespace}:{name}"
        url = reverse(name, args=args, kwargs=kwargs)
        if request.GET:
            param = request.GET.urlencode()
            new_query_dict = QueryDict(mutable=True)
            new_query_dict['_filter'] = param
            url = f'{url}?{new_query_dict.urlencode()}'
        return url

    def reverse_url_add(self, request, *args, **kwargs):
        """
        生成带原搜索条件的添加url
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        return self.reverse_handle_url(request, self.get_url_name_add())

    def reverse_url_change(self, request, obj, *args, **kwargs):
        """
        生成带原搜索条件的修改url
        :param request:
        :param obj:
        :param args:
        :param kwargs:
        :return:
        """
        key = obj.key
        return self.reverse_handle_url(request, self.get_url_name_change(), key=key)

    def reverse_url_delete(self, request, obj, *args, **kwargs):
        """
        生成带原搜索条件的删除url
        :param request:
        :param args:
        :param obj:
        :param kwargs:
        :return:
        """
        key = obj.key
        return self.reverse_handle_url(request, self.get_url_name_delete(), key=key)

    def reverse_url_list(self, request, *args, **kwargs):
        """
        跳转回页面时生成的url
        :param request:
        :param args:
        :param kwargs:
        :return:
        """
        name = f'{self.site.namespace}:{self.get_url_name_list()}'
        list_url = reverse(name)
        params = request.GET.get('_filter')
        if params:
            list_url = f'{list_url}?{params}'
        return list_url

    def save(self, form, is_update=False, *args, **kwargs):
        """
        在使用ModelForm保存数据之前预留的钩子方法
        :param form: form表单实体
        :param is_update: 是否是更新操作标志
        :param args:
        :param kwargs:
        :return:
        """
        save_process = [self.saving_pre_handler, self.saving, self.saving_post_handler]
        for process in save_process:
            result = process(form, is_update, *args, **kwargs)
            if not result:
                return False
        return True

    def saving(self, form, is_update=False, *args, **kwargs):
        """
        在使用ModelForm保存数据之前预留的钩子方法
        :param form: form表单实体
        :param is_update: 是否是更新操作标志
        :param args:
        :param kwargs:
        :return:
        """
        # 是更新操作直接保存
        if is_update:
            form.save()
        # 新建操作需要额外生成key值
        else:
            # 获取form返回的实例，key需要单独生成，所以目前先不进行保存
            obj = form.save(commit=False)
            if not form.cleaned_data.get("key"):
                # TODO 性能问题
                # 确保获取的key唯一
                while True:
                    key = get_random_string(24)
                    if not self.model_class.objects.filter(key=key).exists():
                        break
                # 定义唯一key并进行保存
                obj.key = key
            obj.save()
        return True

    def saving_post_handler(self, form, is_update=False, *args, **kwargs):
        return True

    def saving_pre_handler(self, form, is_update=False, *args, **kwargs):
        return True

    def show_add_button(self, obj=None, is_header=True, *args, **kwargs):
        """
        显示添加按钮
        :param obj:
        :param is_header:
        :param args:
        :param kwargs:
        :return:
        """
        if hasattr(settings, 'PERMISSION_AUTHENTICATE_ENABLE') and settings.PERMISSION_AUTHENTICATE_ENABLE:
            permission_enable = True
            if not hasattr(settings, 'PERMISSION_SESSION_KEY'):
                raise Exception("没有制定PERMISSION_SESSION_KEY信息")
            permission_session = self.request.session.get(settings.PERMISSION_SESSION_KEY, "no_key_exist")
        else:
            permission_enable = False
            permission_session = None
        # 启用权限判断
        if permission_enable:
            # request中有权限session
            if permission_session == "no_key_exist":
                return None
            # 权限session允许url
            elif f"{self.site.namespace}:{self.get_url_name_add()}" not in permission_session:
                return None
        title = "新建"
        if is_header:
            return title
        if obj:
            url = self.reverse_url_add(self.request, *args, **kwargs)
            return {"title": title, "url": url, "key": "list"}
        return f'{title}功能未上线'

    def show_checkbox_field(self, obj=None, is_title=False, *args, **kwargs):
        """
        显示复选框
        :param obj: model object
        :param is_title: 是否显示标题标志
        :param args:
        :param kwargs:
        :return:
        """
        title = "选择"
        if is_title:
            return title
        if obj:
            return obj.key, "body", {"type": "checkbox"}
        return "checkbox function error"

    def show_delete_button(self, obj=None, is_header=True, *args, **kwargs):
        """
        显示删除按钮
        :param obj:
        :param is_header:
        :param args:
        :param kwargs:
        :return:
        """
        if hasattr(settings, 'PERMISSION_AUTHENTICATE_ENABLE') and settings.PERMISSION_AUTHENTICATE_ENABLE:
            permission_enable = True
            if not hasattr(settings, 'PERMISSION_SESSION_KEY'):
                raise Exception("没有制定PERMISSION_SESSION_KEY信息")
            permission_session = self.request.session.get(settings.PERMISSION_SESSION_KEY, "no_key_exist")
        else:
            permission_enable = False
            permission_session = None
        # 启用权限判断
        if permission_enable:
            # request中有权限session
            if permission_session == "no_key_exist":
                return None
            # 权限session允许url
            elif f"{self.site.namespace}:{self.get_url_name_delete()}" not in permission_session:
                return None
        title = "删除"
        if is_header:
            return title
        if obj:
            url = self.reverse_url_delete(self.request, obj=obj, *args, **kwargs)
            return {"title": title, "url": url, "key": "delete"}
        return f'{title}功能未上线'

    def show_edit_button(self, obj=None, is_header=True, *args, **kwargs):
        """
        显示编辑按键
        :param obj:
        :param is_header:
        :param args:
        :param kwargs:
        :return:
        """
        if hasattr(settings, 'PERMISSION_AUTHENTICATE_ENABLE') and settings.PERMISSION_AUTHENTICATE_ENABLE:
            permission_enable = True
            if not hasattr(settings, 'PERMISSION_SESSION_KEY'):
                raise Exception("没有制定PERMISSION_SESSION_KEY信息")
            permission_session = self.request.session.get(settings.PERMISSION_SESSION_KEY, "no_key_exist")
        else:
            permission_enable = False
            permission_session = None
        # 启用权限判断
        if permission_enable:
            # request中有权限session
            if permission_session == "no_key_exist":
                return None
            # 权限session允许url
            elif f"{self.site.namespace}:{self.get_url_name_change()}" not in permission_session:
                return None
        title = "编辑"
        if is_header:
            return title
        if obj:
            url = self.reverse_url_change(self.request, obj=obj, *args, **kwargs)
            return {"title": title, "url": url, "key": "change"}
        return f'{title}功能未上线'

    def show_options_button(self, obj=None, is_header=True, *args, **kwargs):
        """
        显示操作合集
        :param obj: 操作实例
        :param is_header: 是否显示标题信息
        :param args:
        :param kwargs:
        :return:
        """
        if hasattr(settings, 'PERMISSION_AUTHENTICATE_ENABLE') and settings.PERMISSION_AUTHENTICATE_ENABLE:
            permission_enable = True
            if not hasattr(settings, 'PERMISSION_SESSION_KEY'):
                raise Exception("没有制定PERMISSION_SESSION_KEY信息")
            permission_session = self.request.session.get(settings.PERMISSION_SESSION_KEY, "no_key_exist")
        else:
            permission_enable = False
            permission_session = None
        # 启用权限判断
        if permission_enable:
            # request中有权限session
            if permission_session == "no_key_exist":
                return None
            # 权限session允许url
            elif f"{self.site.namespace}:{self.get_url_name_change()}" not in permission_session:
                return None
        title = "操作"
        if is_header:
            return title
        if obj:
            data = list()
            edit_button = self.show_edit_button(obj=obj, is_header=False, *args, **kwargs)
            if edit_button:
                data.append(edit_button)
            del_button = self.show_delete_button(obj=obj, is_header=False, *args, **kwargs)
            if del_button:
                data.append(del_button)
            return data
        return f'{title}按钮显示功能未上线'


class GideonSite(object):
    def __init__(self):
        # 存储注册类、处理函数
        self._registry = []
        self.app_name = 'gideon'
        self.namespace = 'gideon'

    def get_urls(self):
        """
        获取url patterns信息
        :return: 包含url信息的列表
            获取的 url 每条信息格式大致如下：
                model_class.app_label/model_class.model_name/handler.prev
        """
        # 定义生成url信息的列表
        patterns = []
        # 循环生成url
        for item in self._registry:
            # model类
            model_class = item['model_class']
            # 程序处理类
            handler = item['handler']
            # app名称和model名称先拼接成url前一部分
            app_label, model_name = model_class.get_app_label(), model_class.get_model_name()
            # app_label, model_name = model_class._meta.app_label, model_class._meta.model_name
            # 如果自定义prev字段，再拼接上prev字段
            prev = handler.get_prev()
            base_url = f'{app_label}/{model_name}'
            if prev:
                base_url = f"{base_url}/{prev}"
            # patterns.append(path(f'{base_url}/list/', handler.list_view))
            # patterns.append(path(f'{base_url}/add/', handler.add_view))
            # patterns.append(path(f'{base_url}/edit/<int:pk>/', handler.change_view))
            # patterns.append(path(f'{base_url}/del/<int:pk>/', handler.delete_view))
            patterns.append(path(f'{base_url}/', (handler.get_urls(), None, None)))
        # 返回url信息
        return patterns

    @property
    def urls(self):
        """
        获取url信息
        :return: 返回路由信息
        """
        # 分别是 路由路径  app名称  命名空间名称
        return self.get_urls(), self.app_name, self.namespace

    def register(self, model_class, handler_class=None, prev=None, model_form_class=None):
        """
        注册
        :param model_class: 是models中的数据库的相关类
        :param handler_class: 处理请求的视图函数所在的类
        :param prev: 请求的前缀
        :param model_form_class: 表单处理相关类
        :return:
        """
        if not handler_class:
            handler_class = GideonHandler
        # 注册model类以及处理类
        self._registry.append(
            {
                'model_class': model_class,
                'handler': handler_class(self, model_class, prev, model_form_class)
            }
        )


site = GideonSite()
